﻿using AntdUI;
using Serilog;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace WorkTool2.Helpers;

/// <summary>
/// 检查更新
/// </summary>
public static class UpdateHelper
{
    private const string UPDATE_VERSION_JSON = "https://gitcode.com/gcw_E8NZtOkO/tools/releases/download/2.0.0.0/version.json";

    private const string MAIN_EXE_NAME = "WorkTool.exe";

    private const string UPDATE_EXE_NAME = "Update.exe";

    private static string _tempZipPath = string.Empty;
    private static string _remoteUrl = string.Empty;

    private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
    {
        // PropertyNamingPolicy = JsonNamingPolicy.CamelCase,//驼峰
        ReferenceHandler = ReferenceHandler.IgnoreCycles,//忽略循环引用
        IncludeFields = true,//包含成员字段序列化
        PropertyNameCaseInsensitive = true,//不区分大小写
        AllowTrailingCommas = true,//允许尾随逗号
        ReadCommentHandling = JsonCommentHandling.Skip,//允许注释
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,//处理乱码问题
        WriteIndented = true,//是否应使用整齐打印
    };

    /// <summary>
    /// 更新程序
    /// </summary>
    /// <param name="form"></param>
    /// <returns></returns>
    public static async Task Update(Form form)
    {
        try
        {
            if (StartUpdateMain(form, false, "检测到有已下载完成的安装包！是否立即更新？"))
            {
                var update = await CheckForUpdate();
                // 检查更新
                if (update.Item1)
                {
                    if (Modal.open(new Modal.Config(form, "更新提示", $"发现新版本【{update.Item2.Version}】！是否立即更新？{(string.IsNullOrEmpty(update.Item2.Description) ? "" : $"\n本次更新内容：\n{update.Item2.Description}")}", TType.Info)
                    {
                        CancelText = "否",
                        OkText = "是"
                    }) == DialogResult.OK)
                    {
                        await StartUpdater(form, update.Item2);
                    }
                    return;
                }
                else
                {
                    Notification.info(form, "当前已是最新版本！", "", TAlignFrom.Top);
                }
            }
        }
        catch (Exception ex)
        {
            Notification.error(form, "更新错误", ex.Message, TAlignFrom.Top);
        }
    }

    /// <summary>
    /// 检查是否有可用更新
    /// </summary>
    /// <returns></returns>
    private static async Task<(bool, VersionInfo?)> CheckForUpdate()
    {
        try
        {
            using var httpClient = new HttpClient();
            httpClient.Timeout = TimeSpan.FromMinutes(5);
            var latestVersionStr = await httpClient.GetStringAsync(UPDATE_VERSION_JSON);
            var latestInfo = JsonSerializer.Deserialize<VersionInfo>(latestVersionStr, _jsonSerializerOptions);
            var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
            var latestVersion = new Version(latestInfo.Version);
            var isUpdate = false;
            var oldUpdate = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, UPDATE_EXE_NAME);
            if (File.Exists(oldUpdate))
            {
                var oldUpdateVersionInfo = FileVersionInfo.GetVersionInfo(oldUpdate);
                var currentUpdateVersion = new Version(oldUpdateVersionInfo.FileVersion);
                var latestUpdateVersion = new Version(latestInfo.UpdateVersion);
                if (latestUpdateVersion.CompareTo(currentUpdateVersion) > 0)
                {
                    isUpdate = true;
                }
            }
            else
            {
                isUpdate = true;
            }
            if (latestVersion.CompareTo(currentVersion) > 0 || isUpdate)
            {
                return (true, latestInfo);
            }
            return (false, null);
        }
        catch (Exception ex) when (ex is FileNotFoundException || ex is DirectoryNotFoundException)
        {
            Log.Error(ex, $"检查更新时出错: {ex.Message}");
            throw;
        }
        catch (Exception ex)
        {
            Log.Error(ex, ex.Message);
            throw;
        }
    }

    /// <summary>
    /// 开始更新
    /// </summary>
    /// <param name="form"></param>
    /// <param name="versionInfo"></param>
    /// <param name="isForcedUpdate">是否强制更新</param>
    /// <returns></returns>
    public static async Task StartUpdater(Form form, VersionInfo versionInfo, bool isForcedUpdate = false)
    {
        var main = (form as Main);
        try
        {
            main.InvokeIfRequired(() =>
            {
                main.UpdateVisibleShow();
                main.UpdateProgressValue = 0;
            });
            var oldUpdate = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, UPDATE_EXE_NAME);
            if (File.Exists(oldUpdate))
            {
                var oldUpdateVersionInfo = FileVersionInfo.GetVersionInfo(oldUpdate);
                var currentUpdateVersion = new Version(oldUpdateVersionInfo.FileVersion);
                var latestUpdateVersion = new Version(versionInfo.UpdateVersion);
                if (latestUpdateVersion.CompareTo(currentUpdateVersion) > 0)
                {
                    await UpdateExe(main, versionInfo);
                }
            }
            else
            {
                await UpdateExe(main, versionInfo);
            }
            main.InvokeIfRequired(() =>
            {
                main.UpdateProgressValue = 0;
            });
            var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
            var latestVersion = new Version(versionInfo.Version);
            if (latestVersion.CompareTo(currentVersion) > 0)
            {
                await UpdateMain(main, versionInfo, isForcedUpdate);
            }
            main.InvokeIfRequired(() =>
            {
                main.UpdateProgressValue = 1;
            });
        }
        catch (Exception ex)
        {
            Log.Error(ex, ex.Message);
            Modal.open(new Modal.Config(form, "错误", ex.Message, TType.Error)
            {
                CancelText = null,
                OkType = TTypeMini.Error,
                OkText = "知道了"
            });
        }
        finally
        {
            main.InvokeIfRequired(() =>
            {
                main.UpdateVisibleHide();
            });
        }
    }

    /// <summary>
    /// 开始更新
    /// </summary>
    /// <param name="form"></param>
    /// <param name="versionInfo"></param>
    /// <param name="isForcedUpdate">是否强制更新</param>
    /// <returns></returns>
    public static async Task ManagerStartUpdater(Form form, VersionInfo versionInfo, bool isForcedUpdate = false)
    {
        var main = (form as Main);
        try
        {
            main.InvokeIfRequired(() =>
            {
                main.UpdateVisibleShow();
                main.UpdateProgressValue = 0;
            });
            await UpdateExe(main, versionInfo);
            main.InvokeIfRequired(() =>
            {
                main.UpdateProgressValue = 0;
            });
            await UpdateMain(main, versionInfo, isForcedUpdate);
            main.InvokeIfRequired(() =>
            {
                main.UpdateProgressValue = 1;
            });
        }
        catch (Exception ex)
        {
            Log.Error(ex, ex.Message);
            Modal.open(new Modal.Config(form, "错误", ex.Message, TType.Error)
            {
                CancelText = null,
                OkType = TTypeMini.Error,
                OkText = "知道了"
            });
        }
        finally
        {
            main.InvokeIfRequired(() =>
            {
                main.UpdateVisibleHide();
            });
        }
    }

    /// <summary>
    /// 下载主程序
    /// </summary>
    /// <param name="main"></param>
    /// <param name="versionInfo"></param>
    /// <param name="isForcedUpdate">是否强制更新</param>
    /// <returns></returns>
    public static async Task UpdateMain(Main main, VersionInfo versionInfo, bool isForcedUpdate)
    {
        try
        {
            main.InvokeIfRequired(() =>
            {
                main.UpdateLableText = "正在下载主程序，请耐心等待...";
            });
            var tempZipPath = Path.Combine(Path.GetTempPath(), Path.GetFileName(versionInfo.URL)); // 临时zip路径
            await Download(main, versionInfo.URL, tempZipPath, new CancellationTokenSource().Token);
            _remoteUrl = versionInfo.URL;
            _tempZipPath = tempZipPath;
            StartUpdateMain(main, isForcedUpdate, "主程序已下载完成！是否立即更新？");
        }
        catch (Exception ex)
        {
            Log.Error(ex, "无法启动更新程序");
            throw;
        }
    }

    public static bool StartUpdateMain(Form form, bool isForcedUpdate, string message)
    {
        if (!string.IsNullOrEmpty(_remoteUrl) && !string.IsNullOrEmpty(_tempZipPath))
        {
            var isUpdate = false;
            if (isForcedUpdate)
            {
                isUpdate = true;
            }
            else
            {
                if (Modal.open(new Modal.Config(form, "更新提示", message, TType.Info)
                {
                    CancelText = "否",
                    OkText = "是"
                }) == DialogResult.OK)
                {
                    isUpdate = true;
                }
            }
            if (isUpdate)
            {
                var startInfo = new ProcessStartInfo(UPDATE_EXE_NAME)
                {
                    Arguments = $"\"{_remoteUrl}\" \"{MAIN_EXE_NAME}\" \"{_tempZipPath}\" \"{SettingsHelper._fileSettings.SaveFolder}\"",
                    UseShellExecute = true
                };
                Process.Start(startInfo);
                _remoteUrl = _tempZipPath = string.Empty;
                Application.Exit();
            }
            else
            {
                return isUpdate;
            }
        }
        return true;
    }

    /// <summary>
    /// 下载更新组件
    /// </summary>
    /// <param name="main"></param>
    /// <param name="versionInfo"></param>
    /// <returns></returns>
    public static async Task UpdateExe(Main main, VersionInfo versionInfo)
    {
        main.InvokeIfRequired(() =>
        {
            main.UpdateLableText = "正在下载更新组件，请耐心等待...";
        });
        var tempZipPath = Path.Combine(Path.GetTempPath(), Path.GetFileName(versionInfo.UpdateURL)); // 临时zip路径
        await Download(main, versionInfo.UpdateURL, tempZipPath, new CancellationTokenSource().Token);
        main.InvokeIfRequired(() =>
        {
            main.UpdateLableText = "正在更新组件，请耐心等待...";
        });
        System.IO.Compression.ZipFile.ExtractToDirectory(tempZipPath, AppDomain.CurrentDomain.BaseDirectory, true);
        File.Delete(tempZipPath);
        main.InvokeIfRequired(() =>
        {
            main.UpdateLableText = "更新组件已完成";
        });
    }

    /// <summary>
    /// 下载
    /// </summary>
    /// <param name="main"></param>
    /// <param name="remoteUrl"></param>
    /// <param name="savePath"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task Download(Main main, string remoteUrl, string savePath, CancellationToken cancellationToken)
    {
        using var httpClient = new HttpClient();
        httpClient.Timeout = TimeSpan.FromMinutes(30);
        using var response = await httpClient.GetAsync(remoteUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
        response.EnsureSuccessStatusCode(); // 确保响应成功
        long? totalBytes = response.Content.Headers.ContentLength;
        using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);
        using var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write);
        await ProcessContentStream(main, totalBytes, contentStream, fileStream, cancellationToken);
        contentStream.Close();
        contentStream.Dispose();
        fileStream.Close();
        fileStream.Dispose();
    }

    /// <summary>
    /// 进度显示
    /// </summary>
    /// <param name="main"></param>
    /// <param name="totalBytes"></param>
    /// <param name="contentStream"></param>
    /// <param name="fileStream"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private static async Task ProcessContentStream(Main main, long? totalBytes, Stream contentStream, FileStream fileStream, CancellationToken cancellationToken)
    {
        long totalRead = 0;
        byte[] buffer = new byte[8192]; // 8KB 缓冲区
        bool isMoreToRead = true;
        DateTime lastUpdateTime = DateTime.Now;
        float lastProgress = 0;

        while (isMoreToRead)
        {
            cancellationToken.ThrowIfCancellationRequested();
            int read = await contentStream.ReadAsync(buffer, cancellationToken);
            if (read == 0)
            {
                isMoreToRead = false;
            }
            else
            {
                await fileStream.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
                totalRead += read;

                // 实时更新进度，但限制UI更新频率以避免过于频繁的调用
                if (totalBytes.HasValue)
                {
                    float progressPercentage = ((totalRead * 100) / totalBytes.Value);

                    // 只有当进度有实际变化或超过一定时间间隔时才更新UI
                    if (progressPercentage > lastProgress + 0.1f || (DateTime.Now - lastUpdateTime).TotalMilliseconds > 100)
                    {
                        main.InvokeIfRequired(() =>
                        {
                            main.UpdateProgressValue = progressPercentage / 100;
                        });

                        lastProgress = progressPercentage;
                        lastUpdateTime = DateTime.Now;
                    }
                }
                else
                {
                    // 对于未知大小的文件，使用脉冲式进度显示
                    if ((DateTime.Now - lastUpdateTime).TotalMilliseconds > 500)
                    {
                        int pulseProgress = (int)((totalRead / 1024) % 100); // 基于下载量的简单脉冲

                        main.InvokeIfRequired(() =>
                        {
                            main.UpdateProgressValue = pulseProgress / 100;
                        });
                        lastUpdateTime = DateTime.Now;
                    }
                }
            }
        }
        // 下载完成，确保显示100%
        main.InvokeIfRequired(() =>
        {
            main.UpdateProgressValue = 1;
        });
    }

    #region Json比较

    private static readonly JsonDocumentOptions _jsonDocumentOptions = new()
    {
        AllowTrailingCommas = true,
        CommentHandling = JsonCommentHandling.Skip
    };

    private static readonly JsonWriterOptions _jsonWriterOptions = new()
    {
        Indented = true,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    /// <summary>
    /// 将b.json的结构更新到a.json，保留a.json中已有的值
    /// </summary>
    /// <param name="aPath">a.json文件路径</param>
    /// <param name="bPath">b.json文件路径</param>
    public static void UpdateSetting(string aPath, string bPath)
    {
        // 读取a.json和b.json内容
        string aJson = File.ReadAllText(aPath);
        string bJson = File.ReadAllText(bPath);

        // 解析为JSON文档
        using JsonDocument aDoc = JsonDocument.Parse(aJson, _jsonDocumentOptions);
        using JsonDocument bDoc = JsonDocument.Parse(bJson, _jsonDocumentOptions);

        // 合并结构（以b的结构为基准，保留a的值）
        JsonElement merged = MergeElements(aDoc.RootElement, bDoc.RootElement);

        // 将合并结果写回a.json
        string mergedJson = JsonSerializer.Serialize(merged, _jsonSerializerOptions);
        File.WriteAllText(aPath, mergedJson, Encoding.UTF8);
    }

    /// <summary>
    /// 递归合并两个JSON元素，保留a的值，按b的结构调整
    /// </summary>
    private static JsonElement MergeElements(JsonElement a, JsonElement b)
    {
        // 情况1：b是对象，优先按对象合并
        if (b.ValueKind == JsonValueKind.Object)
        {
            using var memoryStream = new MemoryStream();
            using (var writer = new Utf8JsonWriter(memoryStream, _jsonWriterOptions))
            {
                writer.WriteStartObject();

                // 先处理b中的所有键（保证结构与b一致）
                foreach (var bProp in b.EnumerateObject())
                {
                    string propName = bProp.Name;
                    JsonElement bValue = bProp.Value;

                    // 如果a中存在该键，则递归合并值；否则直接使用b的值
                    if (a.TryGetProperty(propName, out JsonElement aValue))
                    {
                        JsonElement mergedValue = MergeElements(aValue, bValue);
                        writer.WritePropertyName(propName);
                        mergedValue.WriteTo(writer);
                    }
                    else
                    {
                        // a中没有该键，直接添加b的键值
                        bProp.WriteTo(writer);
                    }
                }

                writer.WriteEndObject();
            }

            // 将内存流中的数据解析为JsonElement
            memoryStream.Position = 0;
            using var mergedDoc = JsonDocument.Parse(memoryStream, _jsonDocumentOptions);
            return mergedDoc.RootElement.Clone();
        }

        // 情况2：b是数组，a是对象（完善的对象转数组处理）
        if (b.ValueKind == JsonValueKind.Array && a.ValueKind == JsonValueKind.Object)
        {
            using var memoryStream = new MemoryStream();
            using (var writer = new Utf8JsonWriter(memoryStream, _jsonWriterOptions))
            {
                writer.WriteStartArray();

                // 获取b数组中的所有元素
                var bElements = b.EnumerateArray().ToList();

                JsonElement mergedElement = MergeElements(a, bElements[0]);
                mergedElement.WriteTo(writer);

                writer.WriteEndArray();
            }

            memoryStream.Position = 0;
            using var mergedDoc = JsonDocument.Parse(memoryStream, _jsonDocumentOptions);
            return mergedDoc.RootElement.Clone();
        }

        // 情况3：b是数组，a也是数组（完善的数组元素合并逻辑）
        if (b.ValueKind == JsonValueKind.Array && a.ValueKind == JsonValueKind.Array)
        {
            using var memoryStream = new MemoryStream();
            using (var writer = new Utf8JsonWriter(memoryStream, _jsonWriterOptions))
            {
                writer.WriteStartArray();

                // 先处理到两个数组中长度较短的那个的长度
                var aElements = a.EnumerateArray().ToList();
                var bElements = b.EnumerateArray().ToList();

                // 合并对应索引的元素
                for (int i = 0; i < aElements.Count; i++)
                {
                    if (bElements.Count > 0)
                    {
                        JsonElement mergedElement = MergeElements(aElements[i], bElements[0]);
                        mergedElement.WriteTo(writer);
                    }
                    else
                    {
                        aElements[i].WriteTo(writer);
                    }
                }

                writer.WriteEndArray();
            }

            memoryStream.Position = 0;
            using var mergedDoc = JsonDocument.Parse(memoryStream, _jsonDocumentOptions);
            return mergedDoc.RootElement.Clone();
        }

        // 情况4：其他类型（如字符串、数字等），保留a的值（不被b覆盖）
        return a.ValueKind != JsonValueKind.Undefined ? a.Clone() : b.Clone();
    }

    #endregion
}

public class VersionInfo
{
    /// <summary>
    /// 版本号
    /// </summary>
    public string Version { get; set; }

    /// <summary>
    /// 更新时间
    /// </summary>
    public DateTime PublishDate { get; set; }

    /// <summary>
    /// 文件地址
    /// </summary>
    public string URL { get; set; }

    /// <summary>
    /// 更新描述
    /// </summary>
    public string Description { get; set; }

    /// <summary>
    /// Update.exe版本号
    /// </summary>
    public string UpdateVersion { get; set; }

    /// <summary>
    /// 更新Update.exe的地址
    /// </summary>
    public string UpdateURL { get; set; }
}